import { extend } from '../shared/index'


// 全局变量 

/**
 * 当前激活的 Effect 对象
 */
let activeEffect;

/**
 * 是否应该收集依赖
 */
let shouldTrack;

/**
 *  抽离一个 ReactiveEffect 类，代表 Reactive 的 effect 依赖对象
 */
export class ReactiveEffect {
    private _fn: any;
    public scheduler: any;
    public onStop?: () => void;
    public deps: any = new Set<ReactiveEffect>;

    /**
     * 标识 effect 有没有被 stop
     */
    public active = true;

    constructor(fn, scheduler?) {
        this._fn = fn;
        this.scheduler = scheduler;
    }

    run() {

        // 当前 effect 被停止(stop)，仅执行函数
        if (!this.active) {
            // 将 effect 中的函数 fn 返回值返回出去
            return this._fn();
        }

        // 先标识这个 Effect 函数为最后运行的 effect 
        activeEffect = this;
        // 当前 effect 时激活状态（active = true) , 标识 shouldTrack 为 true （需要收集依赖）
        shouldTrack = true;
        // 将 effect 中的函数 fn 返回值返回出去
        return this._fn();
    }

    stop() {
        if (this.active) {
            // 停止哪个 effect 则删除哪个 effect, 而不是删除所有;
            cleanupEffect(this);
            // 改变 effect 激活状态为 stop （停止）
            this.active = false;
            // 依赖收集标识改为不收集依赖
            shouldTrack = false;
        }
    }
}

function cleanupEffect(effect: ReactiveEffect) {
    effect.deps.delete(effect);
    if (effect.onStop) {
        effect.onStop();
    }
}


export function effect(fn: Function, options: any = {}) {
    let scheduler = options?.scheduler;

    // 第一次进来调用 fn
    const _effect = new ReactiveEffect(fn, scheduler);
    // 利用 Object.assign 将 options 内的属性 挂载到 _effect 对象中，比如：onStop
    // Object.assign(_effect, options);
    extend(_effect, options);

    _effect.run();

    // 实现返回一个 effect 函数本身的返回值
    // 使用 bind 改变 run 函数中的 this 指向为 _effect 变量
    const runner: any = _effect.run.bind(_effect);

    // 将 _effect 实例挂载到 runner 上
    runner.effect = _effect;
    return runner;
}

// 响应式对象容器
const targetMap = new Map<Object, Map<string, Set<ReactiveEffect>>>();

/**
 * 收集依赖
 * @param target 响应式对象
 * @param key 响应式对象操作的 key 值
 * @returns 
 */
export function track(target, key) {

    if (!shouldTrack) {
        // 是否收集依赖标识为 false， 则停止收集
        return;
    }

    // 依赖收集的思想
    // target -> key -> dep
    // 1. 存储一个全局的容器，这个容器的 key 值为 target （也就是某个响应式对象，粒度到响应式对象）
    // 2. 响应式容器的 value 存储 一些子容器，子容器的 key 值为 target.key （也就是响应式对象的的属性，粒度到响应式对象的属性）
    // 3. 子容器的 value 值存储一个 set 数组，（为了让 effect 副作用函数不重复）


    // 基于 target 去获取一个 depsMap 容器
    let depsMap = targetMap.get(target);
    if (!depsMap) {
        // 初始化 depsMap 容器
        depsMap = new Map();
        targetMap.set(target, depsMap);
    }

    let deps = depsMap.get(key);
    if (!deps) {
        deps = new Set();
        (depsMap as Map<any, any>).set(key, deps);
    }

    // 收集依赖
    trackEffect(deps)
}


/**
 * 收集依赖
 * @param deps 依赖集合容器
 */
export function trackEffect(deps) {
    if (!activeEffect) {
        // 如果没有 effect 副作用函数，则停止收集
        return;
    }

    // 把激活状态的 effect 存储进 deps 依赖集合
    deps.add(activeEffect);

    // 添加 deps 的引用到 当前收集到的 effect 上
    // 用于 stop 的时，通过 effect 找到所有的其他 deps （粒度到响应式对象的属性，也可以向上继续找到 target 层面，响应式对象层）
    // 找到 deps 后执行删除，通过当前的 effect 对象进行删除，这样就实现了 stop 功能 
    activeEffect.deps = deps;
}


/**
 * 触发所有 effect 
 * @param deps 依赖集合容器
 */
export function triggerEffect(deps) {
    deps?.forEach((effect, i) => {
        // 循环触发 所有 deps（依赖）的 effect 副作用函数
        if (!effect.scheduler)
            effect.run();
        else
            effect.scheduler();
    })
}

/**
 * 触发依赖，依赖调用
 * @param target 
 * @param key 
 */
export function trigger(target, key) {
    const depsMap = targetMap.get(target);

    const deps = depsMap?.get(key);
    triggerEffect(deps);
}


export function stop(runner) {
    // 通过 runner 找到 runner 挂载的 effect
    let effect: ReactiveEffect = runner.effect;
    effect.stop();
}